﻿//************************************************************************
//* Written by Jirat Boomuang
//* Filename: AdaReadWriteCSV.cs
//* Revision: 0.1
//* Author: JiratB
//* Platform: .NET Framework 2.0
//* Description: Utilities around CSV files
//* Note: Ada is my lovely daughter.
//************************************************************************

//************************************************************************
//* Revision History
//------------------------------------------------------------------------
//* Revision  Date      Signature    Description
//------------------------------------------------------------------------
//* 0.1       20130525  JiratB       - First Draft
//************************************************************************

using System;
using System.Collections.Generic;
using System.Text;

using System.IO; //-- for StreamWriter and StreamReader --

namespace AdaCSVUtilities
{
    public class AdaCells : List<string>
    {
        public string cell { get; set; }
    }//-- End of AdaCells class --

    public class AdaColumn
    {
        public string columnName { get; set; }
        public int columnIndex { get; set; }
        public int cellCount { get; set; }
        public AdaCells cells;

        public AdaColumn()
        {
            cells = new AdaCells();
        }
    }//-- End of AdaColumn class --

    public class AdaColumns : List<AdaColumn>
    {
        public AdaColumn column { get; set; }
    }//-- End of AdaColumns class --

    public class AdaCSVRow : List<string>
    {
        public string cell { get; set; }
    }//-- End of CSVRow class --

    public class AdaReadCSV : StreamReader
    {
        #region Constructors
        public AdaReadCSV(Stream stream)
            : base(stream)
        {
        }

        public AdaReadCSV(string filename)
            : base(filename)
        {
        }
        #endregion

        public bool ReadRow(AdaCSVRow row)
        {
            //-- Read the whole line from the input stream --
            row.cell = ReadLine();

            //-- Terminate this method if the line is empty --
            if (String.IsNullOrEmpty(row.cell))
                return false;

            int stringIndex = 0;
            int columns = 0;

            #region While there are charaters left to be analyzed 
            while (stringIndex < row.cell.Length)
            {
                string value;

                #region If quoted field
                // Special handling for quoted field
                if (row.cell[stringIndex] == '"')
                {
                    // Skip initial quote
                    stringIndex++;

                    // Parse quoted value
                    int startIndex = stringIndex;
                    while (stringIndex < row.cell.Length)
                    {
                        // Test for quote character
                        if (row.cell[stringIndex] == '"')
                        {
                            // Found one
                            stringIndex++;

                            // If two quotes together, keep one
                            // Otherwise, indicates end of value
                            if ( 
                                stringIndex >= row.cell.Length //-- Last character --
                                || 
                                row.cell[stringIndex] != '"') //-- End quote --
                            {
                                stringIndex--; //-- Move cursor back by one --
                                break;
                            }
                        }
                        stringIndex++;
                    }
                    //-- Extract the actual value of the cell --
                    value = row.cell.Substring(startIndex, stringIndex - startIndex);

                    //-- Replace " with "\ --
                    value = value.Replace("\"\"", "\"");
                }//-- End of if quote field --
#endregion
                #region If not quoted field
                else
                {
                    // Parse unquoted value
                    int startIndex = stringIndex;
                    while (
                        stringIndex < row.cell.Length 
                        && 
                        row.cell[stringIndex] != ','
                        )
                        stringIndex++;
                    value = row.cell.Substring(startIndex, stringIndex - startIndex);
                }
                #endregion

                // Add field to list
                if (columns < row.Count) //-- If # of column is lesser than what is already there --
                    row[columns] = value;
                else //-- If # of column is greater, create a new one --
                    row.Add(value);

                columns++; //-- Count columns --

                // Eat up to and including next comma
                while (stringIndex < row.cell.Length && row.cell[stringIndex] != ',')
                    stringIndex++;
                if (stringIndex < row.cell.Length)
                    stringIndex++;
            }//-- End of while there are charecters left to be parsed --
            #endregion

            // Delete any unused items
            while (row.Count > columns)
                row.RemoveAt(columns);

            // Return true if any columns read
            return (row.Count > 0);
        }//-- End of ReadRow() --

        public bool ReadFile(AdaColumns columns,  Boolean hasHeader)
        {
            try
            {
                AdaCSVRow row = new AdaCSVRow();

                //-- Read Head --
                if (hasHeader)
                {
                    ReadRow(row);

                    foreach (string s in row)
                    {
                        AdaColumn col = new AdaColumn();

                        col.columnName = s;
                        col.cellCount = 0;

                        columns.Add(col);
                    }
                }//-- End of if (hasHeader) --
                else
                {
                    return false;
                }

                while (ReadRow(row))
                {
                    int i = 0;

                    foreach (string s in row)
                    {
                        AdaColumn col = new AdaColumn();

                        columns[i].cellCount++;
                        columns[i].cells.Add(s);

                        ++i;
                    }//-- End of foreach --
                    
                }//-- End of while --
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }//-- End of ReadFile() --
    }//-- End of AdaReadCSV class --

    public class AdaWriteCSV : StreamWriter
    {
        #region Constructors
        public AdaWriteCSV(Stream stream)
            : base(stream)
        {
        }

        public AdaWriteCSV(string filename)
            : base(filename)
        {
        }
        #endregion

        public void WriteRow(AdaCSVRow row)
        {
            StringBuilder line = new StringBuilder();
            bool firstColumn = true; // First column flag; for not adding the separator

            foreach (string value in row)
            {
                // Add comma separator
                if (!firstColumn)
                    line.Append(',');

                // Manage values that contain comma or quote
                if (value.IndexOfAny(new char[] { '"', ',' }) != -1)
                    line.AppendFormat("\"{0}\"", value.Replace("\"", "\"\""));
                else
                    line.Append(value);

                firstColumn = false; // Reset first column flag
            }
            row.cell = line.ToString();
            WriteLine(row.cell);
        }//-- End of WriteRow() --
    }//-- End of AdaWriteCSV class --

    public class AdaReadWriteCSV
    {
    }//-- End of AdaReadWriteCSV class --

    public class AdaCSVUtils
    {
        public static string GetStringValue(int index, AdaColumns columns, string columnName)
        {
            foreach (AdaColumn col in columns)
            {
                if (col.columnName == columnName)
                {
                    return col.cells[index];
                }
            }//-- End of foreach --

            return "";
        }//-- End of GetStringValue() --
    }//-- End of AdaCSVUtils class --
}//-- End of namespace AdaCSVUtilities --

//************************************************************************
// End of file
//************************************************************************
